package br.edu.unifei.cct720.trabalho08.modelo.transformacao;

import java.util.ArrayList;

import br.edu.unifei.cct720.trabalho08.modelo.formas.Objeto;
import br.edu.unifei.cct720.trabalho08.modelo.ponto.Ponto2D;
import br.edu.unifei.cct720.trabalho08.modelo.ponto.Ponto3D;

public class Transformacao3D {
	
	// Declaracao de variaveis de instancia
	private static double matriz[][] = new double[4][4];
	private static Ponto3D pontoReferencia = null;

	// Enum que armazena os eixos de coordenadas
	public enum Eixo {
		X, Y, Z;
	}

	/**
	 * Faz a translacao do objeto
	 * 
	 * @param antigo
	 * @param dx
	 * @param dy
	 * @param dz
	 * @param referencia
	 * @return
	 */
	public static Objeto transladar(
			Objeto antigo, 
			int dx, 
			int dy, 
			int dz,
			Ponto3D referencia
	) {
		matriz[0][0] = 1;
		matriz[0][1] = 0;
		matriz[0][2] = 0;
		matriz[0][3] = dx;
		matriz[1][0] = 0;
		matriz[1][1] = 1;
		matriz[1][2] = 0;
		matriz[1][3] = dy;
		matriz[2][0] = 0;
		matriz[2][1] = 0;
		matriz[2][2] = 1;
		matriz[2][3] = dz;
		matriz[3][0] = 0;
		matriz[3][1] = 0;
		matriz[3][2] = 0;
		matriz[3][3] = 1;

		pontoReferencia = referencia;

		ArrayList<Ponto3D> listaTransladada = aplicarTransformacao(antigo
				.getLista3D());

		return new Objeto(listaTransladada);
	}

	/**
	 * Faz a rotacao do objeto
	 * 
	 * @param antigo
	 * @param angulo
	 * @param referencia
	 * @param tipo
	 * @return
	 */
	public static Objeto rotacionar(Objeto antigo, int angulo,
			Ponto3D referencia, Eixo tipo) {
		double radianos = Math.toRadians(angulo);

		if (tipo == Eixo.Z) {
			matriz[0][0] = Math.cos(radianos);
			matriz[0][1] = -1 * Math.sin(radianos);
			matriz[0][2] = 0;
			matriz[0][3] = 0;

			matriz[1][0] = Math.sin(radianos);
			matriz[1][1] = Math.cos(radianos);
			matriz[1][2] = 0;
			matriz[1][3] = 0;

			matriz[2][0] = 0;
			matriz[2][1] = 0;
			matriz[2][2] = 1;
			matriz[2][3] = 0;

			matriz[3][0] = 0;
			matriz[3][1] = 0;
			matriz[3][2] = 0;
			matriz[3][3] = 1;
		} else if (tipo == Eixo.X) {
			matriz[0][0] = 1;
			matriz[0][1] = 0;
			matriz[0][2] = 0;
			matriz[0][3] = 0;

			matriz[1][0] = 0;
			matriz[1][1] = Math.cos(radianos);
			matriz[1][2] = -1 * Math.sin(radianos);
			matriz[1][3] = 0;

			matriz[2][0] = 0;
			matriz[2][1] = Math.sin(radianos);
			matriz[2][2] = Math.cos(radianos);
			matriz[2][3] = 0;

			matriz[3][0] = 0;
			matriz[3][1] = 0;
			matriz[3][2] = 0;
			matriz[3][3] = 1;
		} else if (tipo == Eixo.Y) {
			matriz[0][0] = Math.cos(radianos);
			matriz[0][1] = 0;
			matriz[0][2] = -1 * Math.sin(radianos);
			matriz[0][3] = 0;

			matriz[1][0] = 0;
			matriz[1][1] = 1;
			matriz[1][2] = 0;
			matriz[1][3] = 0;

			matriz[2][0] = Math.sin(radianos);
			matriz[2][1] = 0;
			matriz[2][2] = Math.cos(radianos);
			matriz[2][3] = 0;

			matriz[3][0] = 0;
			matriz[3][1] = 0;
			matriz[3][2] = 0;
			matriz[3][3] = 1;
		}

		pontoReferencia = referencia;

		ArrayList<Ponto3D> listaRotacionada = aplicarTransformacao(antigo
				.getLista3D());

		return new Objeto(listaRotacionada);
	}

	/**
	 * Aplica a transformacao ao objeto
	 * 
	 * @param lista3D
	 * @return
	 */
	private static ArrayList<Ponto3D> aplicarTransformacao(
			ArrayList<Ponto3D> lista3D) {
		ArrayList<Ponto3D> novaLista = new ArrayList<Ponto3D>();

		for (Ponto3D ponto3D : lista3D) {
			novaLista.add(transforma(ponto3D));
		}

		return novaLista;
	}

	/**
	 * Transforma um ponto 3D
	 * 
	 * @param p
	 * @return
	 */
	private static Ponto3D transforma(Ponto3D p) {
		double aux[][] = { { p.getX() - pontoReferencia.getX() },
				{ p.getY() - pontoReferencia.getY() },
				{ p.getZ() - pontoReferencia.getZ() }, { 1 } };
		double result[][] = new double[4][1];
		double soma;

		for (int i = 0; i < 4; i++) {
			soma = 0;
			for (int j = 0; j < 4; j++) {
				soma += matriz[i][j] * aux[j][0];
			}
			result[i][0] = soma;
		}

		return new Ponto3D(result[0][0] + pontoReferencia.getX(), result[1][0]
				+ pontoReferencia.getY(), result[2][0] + pontoReferencia.getY());
	}

	/**
	 * Converte um ponto 2D para 3D
	 * 
	 * @param ponto3D
	 * @return
	 */
	public static Ponto2D converte3Dpara2D(Ponto3D ponto3D) {
		int x = ponto3D.getX() + ponto3D.getZ()
				* (int) Math.round(Math.cos(Math.toRadians(45)));
		int y = ponto3D.getY() + ponto3D.getZ()
				* (int) Math.round(Math.sin(Math.toRadians(45)));

		return new Ponto2D(x, y);
	}
}
